package cn.ibizlab.util.security;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

public interface AuthenticationUser extends UserDetails
{

	@Override
	default String getUsername() {
		return (String)get("username");
	}

	default void setUsername(String username) {
		put("username",username);
	}

	default String getDisplayName() {
		return (String)get("display_name|personname");
	}

	default void setDisplayName(String displayName) {
		put("display_name",displayName);
	}

	default String getId() {
		return (String)get("id|userid|username");
	}

	default void setId(String id) {
		put("id",id);
	}

	default String getDc() {
		return (String)get("dc|srfdcid|domain|tenant|tenant_id");
	}

	default void setDc(String dc) {
		put("dc",dc);
	}

	default String getOrganizationId() {
		return (String)get("organization_id|orgid|srforgid");
	}

	default void setOrganizationId(String organizationId) {
		put("organization_id",organizationId);
	}

	default String getOrganizationNumber() {
		return (String)get("organization_number|orgcode|srforgcode");
	}

	default void setOrganizationNumber(String organizationNumber) {
		put("organization_number",organizationNumber);
	}

	default String getOrganizationName() {
		return (String)get("organization_name|orgname|srforgname");
	}

	default void setOrganizationName(String organizationName) {
		put("organization_name",organizationName);
	}

	default String getDepartmentId() {
		return (String)get("department_id|mdeptid|srforgsectorid");
	}

	default void setDepartmentId(String departmentId) {
		put("department_id",departmentId);
	}

	default String getDepartmentNumber() {
		return (String)get("department_number|mdeptcode|srforgsectorcode");
	}

	default void setDepartmentNumber(String departmentNumber) {
		put("department_number",departmentNumber);
	}

	default String getDepartmentName() {
		return (String)get("department_name|mdeptname|srforgsectorname");
	}

	default void setDepartmentName(String departmentName) {
		put("department_name",departmentName);
	}

	default String getOrganizationalRoleId() {
		return (String)get("organizational_role_id|curteamid");
	}

	default void setOrganizationalRoleId(String organizationalRoleId) {
		put("organizational_role_id",organizationalRoleId);
	}

	default String getOrganizationalRoleName() {
		return (String)get("organizational_role_name|curteamname");
	}

	default void setOrganizationalRoleName(String organizationalRoleName) {
		put("organizational_role_name",organizationalRoleName);
	}

	default String getDeploySystemId() {
		return (String)get("deploy_system_id|dcsystemid|srfdcsystemid");
	}

	default void setDeploySystemId(String deploySystemId) {
		put("deploy_system_id",deploySystemId);
	}

	default String getSystemId() {
		return (String)get("system_id|srfsystemid");
	}

	default void setSystemId(String systemId) {
		put("system_id",systemId);
	}

	default String getEmployeeNumber() {
		return (String)get("employee_number|usercode");
	}

	default void setEmployeeNumber(String employeeNumber) {
		put("employee_number",employeeNumber);
	}

	default String getEmployeeType() {
		return (String)get("employee_type");
	}

	default void setEmployeeType(String employeeType) {
		put("employee_type",employeeType);
	}

	default String getTitle() {
		return (String)get("title|postname");
	}

	default void setTitle(String title) {
		put("title",title);
	}

	default String getMail() {
		return (String)get("mail|email");
	}

	default void setMail(String mail) {
		put("mail",mail);
	}

	default String getTelephoneNumber() {
		return (String)get("telephone_number|phone");
	}

	default void setTelephoneNumber(String telephoneNumber) {
		put("telephone_number",telephoneNumber);
	}

	default String getMobile() {
		return (String)get("mobile|phone");
	}

	default void setMobile(String mobile) {
		put("mobile",mobile);
	}

	default String getIdentificationNumber() {
		return (String)get("identification_number|certcode");
	}

	default void setIdentificationNumber(String identificationNumber) {
		put("identification_number",identificationNumber);
	}

	default String getPostalAddress() {
		return (String)get("postal_address|addr");
	}

	default void setPostalAddress(String postalAddress) {
		put("postal_address",postalAddress);
	}

	default String getDescription() {
		return (String)get("description|memo");
	}

	default void setDescription(String description) {
		put("description",description);
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default boolean isSuperUser() {
		return Integer.valueOf(1).equals(get("superuser"));
	}

	default void setSuperUser(Integer superUser) {put("superuser",superUser);}

	@JsonIgnore
	@JSONField(serialize = false)
	default boolean isApiUser() {
		return Integer.valueOf(1).equals(get("apiuser"));
	}

	default void setApiUser(Integer apiUser) {put("apiuser",apiUser);}

	default String getDeviceInfo() {
		return (String)get("device_info|user_agent");
	}

	default void setDeviceInfo(String deviceInfo) {
		put("device_info",deviceInfo);
	}

	default String getClientIp() {
		return (String)get("client_ip|ip|referer");
	}

	default void setClientIp(String clientIp) {
		put("client_ip",clientIp);
	}

	@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", locale = "zh" , timezone="GMT+8")
	@JSONField(format="yyyy-MM-dd HH:mm:ss")
	default Date getExpiration() {
		return (Date)get("expiration");
	}

	default void setExpiration(Date expiration) {put("expiration",expiration);}

	default String getToken() {
		return (String)get("token");
	}

	default void setToken(String token) {put("token",token);}

	@JsonIgnore
	@JSONField(serialize = false)
	@Override
	default boolean isAccountNonExpired() {
		return getExpiration()==null?true:(!getExpiration().before(new Date()));
	}

	@JsonIgnore
	@JSONField(serialize = false)
	@Override
	default boolean isAccountNonLocked() {
		return true;
	};

	@JsonIgnore
	@JSONField(serialize = false)
	@Override
	default boolean isCredentialsNonExpired() {
		return true;
	}

	@JsonIgnore
	@JSONField(serialize = false)
	@Override
	default String getPassword() {
		return null;
	}

	default void setPassword(String password) {
		put("password",password);
	}

	@Override
	default boolean isEnabled() {
		return true;
	}

	@JsonIgnore
	@JSONField(serialize = false)
	Collection<GrantedAuthority> getAuthorities();

	@JsonIgnore
	@JSONField(serialize = false)
	Map<String, Object> getExtensionparams();

	@JsonIgnore
	@JSONField(serialize = false)
	default Map getSessionParams() {
		return getExtensionparams();
	}


	@JsonAnyGetter
	@JSONField(name = "_any", unwrapped = true, serialize = true, deserialize = false)
	default Map<String , Object> any() {return getExtensionparams();}

	@JsonAnySetter
	@JSONField(name = "_any", unwrapped = true, serialize = false, deserialize = true)
	default void set(String field, Object value) {
		if(getExtensionparams()!=null)
			getExtensionparams().put(field,value);
	}

	default void put(String field, Object value) {set(field,value);}

	default Object get(String field) {
		return get(field,null);
	}

	default Object get(String field,Object defaultValue) {
		if(any()!=null) {
			for(String key:field.split("[|]")) {
				Object obj=any().get(key);
				if(obj!=null)
					return obj;
			}
		}
		return defaultValue;
	}

	default void readHeader(boolean change) {}

	default void setMemberOf(List<AuthMember> memberOf) {put("member_of",memberOf);}

	default List<AuthMember> getMemberOf() {
		return (List)get("member_of",new ArrayList<AuthMember>());
	}

	default void setCurMemberOf(AuthMember curMemberOf) {
		put("cur_member_of",curMemberOf);
		if(curMemberOf!=null) {
			this.setOrganizationId(curMemberOf.getOrganizationId());
			this.setOrganizationNumber(curMemberOf.getOrganizationNumber());
			this.setOrganizationName(curMemberOf.getOrganizationName());
			this.setDepartmentId(curMemberOf.getDepartmentId());
			this.setDepartmentNumber(curMemberOf.getDepartmentNumber());
			this.setDepartmentName(curMemberOf.getDepartmentName());
			this.setOrganizationalRoleId(curMemberOf.getOrganizationalRoleId());
			this.setOrganizationalRoleName(curMemberOf.getOrganizationalRoleName());
			if(!ObjectUtils.isEmpty(curMemberOf.getDc()))
				this.setDc(curMemberOf.getDc());
		}
	}

	default AuthMember getCurMemberOf() {
		return (AuthMember) get("cur_member_of",!ObjectUtils.isEmpty(getMemberOf())?getMemberOf().get(0):null);
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set<String> getCurOrgIds() {
		if(getMemberOf()!=null)
			return new LinkedHashSet(getMemberOf().stream().filter(e->!ObjectUtils.isEmpty(e.getOrganizationId())).map(e -> e.getOrganizationId()).collect(java.util.stream.Collectors.toList()));
		return new LinkedHashSet<>(Arrays.asList(getOrganizationId()));
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set getParentOrgIds() {
		return new HashSet();
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set getSubOrgIds() {
		return getCurOrgIds();
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set<String> getCurDeptIds() {
		if(getMemberOf()!=null)
			return new LinkedHashSet(getMemberOf().stream().filter(e->!ObjectUtils.isEmpty(e.getDepartmentId())).map(e -> e.getDepartmentId()).collect(java.util.stream.Collectors.toList()));
		return new LinkedHashSet<>(Arrays.asList(getDepartmentId()));
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set getParentDeptIds() {
		return new HashSet();
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set getSubDeptIds() {
		return getCurDeptIds();
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default Set<String> getCurTeamIds() {
		if(getMemberOf()!=null)
			return new LinkedHashSet(getMemberOf().stream().filter(e->!ObjectUtils.isEmpty(e.getOrganizationalRoleId())).map(e -> e.getOrganizationalRoleId()).collect(java.util.stream.Collectors.toList()));
		return new LinkedHashSet<>(Arrays.asList(getOrganizationalRoleId()));
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default String getPersonName() {
		return getDisplayName();
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default String getUserid() {
		return getId();
	}

	static AuthenticationUser getAuthenticationUser()
	{
		if(SecurityContextHolder.getContext()==null||SecurityContextHolder.getContext().getAuthentication()==null||SecurityContextHolder.getContext().getAuthentication().getPrincipal()==null){
			return new BaseImpl();
		}
		Object userDetails = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		AuthenticationUser authuserdetail;
		if(userDetails instanceof AuthenticationUser) {
			authuserdetail= (AuthenticationUser)userDetails;
		}
		else if (userDetails instanceof String) {
			authuserdetail=new BaseImpl();
			authuserdetail.setId(userDetails.toString());
			authuserdetail.setUsername(userDetails.toString());
		}
		else if("net.ibizsys.central.cloud.core.security.AuthenticationUser".equals(userDetails.getClass().getName())){
			authuserdetail = AuthenticationContextHolder.getContext();
		}
		else {
			authuserdetail = BaseImpl.from(userDetails);
		}
		return authuserdetail;
	}

	static AuthenticationUser setAuthenticationUser(AuthenticationUser user) {
		UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
		SecurityContextHolder.getContext().setAuthentication(authentication);
		return user;
	}

	class BaseImpl implements AuthenticationUser {

		public static Class<? extends AuthenticationUser> defaultUserClass = BaseImpl.class;

		static ThreadLocal<AuthenticationUser> contextHolder = new ThreadLocal();

		static ObjectMapper mapper = new ObjectMapper();

		synchronized static AuthenticationUser from(Object obj) {
			AuthenticationUser ctx = contextHolder.get();
			if (ctx == null) {
				try {
					Object user = obj;
					if(!obj.getClass().isAssignableFrom(defaultUserClass))
						user = mapper.readValue(mapper.writeValueAsString(obj), BaseImpl.class);
					ctx = mapper.readValue(mapper.writeValueAsString(user), defaultUserClass);
				} catch (JsonProcessingException e) {
					return null;
				}
				contextHolder.set(ctx);
			}
			return ctx;
		}

		@Override
		@JsonIgnore
		@JSONField(serialize = false)
		public Collection<GrantedAuthority> getAuthorities() {
			return null;
		}
		@JsonIgnore
		@JSONField(serialize = false)
		private Map extensionparams;

		@Override
		public Map getExtensionparams() {
			if(extensionparams==null) {
				extensionparams = new HashMap();
				ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
				if(requestAttributes!=null) {
					HttpServletRequest request = requestAttributes.getRequest();
					if (request != null) {
						Enumeration<String> headerNames = request.getHeaderNames();
						if (headerNames != null) {
							while (headerNames.hasMoreElements()) {
								String name = headerNames.nextElement();
								extensionparams.put(name,request.getHeader(name));
							}
						}
					}
				}
			}
			return extensionparams;
		}
	}

}
